<!-- eslint-disable no-console -->
<script lang="ts" setup>
import type { UnwrapRef } from 'vue';

import { onMounted, reactive, ref } from 'vue';

import { PageHeader } from 'ant-design-vue';
import axios from 'axios';
import { MultiDirectedGraph } from 'graphology';
import Sigma from 'sigma';
import {
  EdgeArrowProgram,
  EdgeDoubleArrowProgram,
  EdgeRectangleProgram,
} from 'sigma/rendering';

const open = ref<boolean>(false);

const showDrawer = () => {
  open.value = true;
};

interface FormState {
  user: string;
  password: string;
}
const formState: UnwrapRef<FormState> = reactive({
  user: '',
  password: '',
});

interface NodeInfo {
  name: string;
  lid: number;
}

const nodeInfo: UnwrapRef<NodeInfo> = reactive({
  name: '',
  lid: 0,
});

// const handleFinish: FormProps['onFinish'] = (values) => {
//   // console.log(values, formState);
// };
// const handleFinishFailed: FormProps['onFinishFailed'] = (errors) => {
//   // console.log(errors);
// };

const graph = new MultiDirectedGraph();

onMounted(async () => {
  const res = await axios.get('/api/topology/get');
  // console.log(res);
  const topo = res.data.data;

  let caIdx = 0;
  Object.values(topo.nodes)
    .filter((n) => n.type === 'ca')
    .map((n) => ({
      ...n,
      idx: n.name.includes('ZS-')
        ? Number(n.name.split('ZS-')[1].split(' ')[0])
        : Number.MAX_VALUE,
    }))
    .sort((n1, n2) => n1.idx - n2.idx)
    .forEach((n, idx) => {
      caIdx = idx % 8;
      graph.addNode(n.id, {
        label: n.name,
        x: Math.floor(idx / 8) * 8,
        y: -100 - caIdx * 6,
        size: 4,
        _type: 'ca',
      });
    });
  Object.values(topo.nodes)
    .filter((n) => n.name.includes('JR'))
    .sort((n1, n2) => (n1.name < n2.name ? -1 : 0))
    .forEach((n, idx) => {
      graph.addNode(n.id, {
        label: n.name,
        x: idx * 32,
        y: 0,
        size: 8,
        _type: 'sw',
      });
    });
  Object.values(topo.nodes)
    .filter((n) => n.name.includes('HJ'))
    .sort((n1, n2) => (n1.name < n2.name ? -1 : 0))
    .forEach((n, idx) => {
      graph.addNode(n.id, {
        label: n.name,
        x: idx * 32,
        y: 100,
        size: 8,
        _type: 'sw',
      });
    });
  Object.values(topo.nodes)
    .filter((n) => n.name.includes('HX'))
    .sort((n1, n2) => {
      const [n1a, n1b] = n1.name
        .slice(n1.name.lastIndexOf('-') + 1)
        .split('/')
        .map(Number);
      const [n2a, n2b] = n2.name
        .slice(n2.name.lastIndexOf('-') + 1)
        .split('/')
        .map(Number);
      return n1a === n2a ? n1b - n2b : n1a - n2a;
    })
    .forEach((n, idx) => {
      graph.addNode(n.id, {
        label: n.name,
        x: idx * 32,
        y: 200,
        size: 8,
        _type: 'sw',
      });
    });

  topo.edges.forEach((e) => {
    graph.addDirectedEdgeWithKey(
      `${e.source}:${e.p1}->${e.target}:${e.p2}`,
      e.source,
      e.target,
      { size: 2 },
    );
  });

  console.log(graph.nodes().length);

  const sigma = new Sigma(graph, document.querySelector('#cy'), {
    itemSizesReference: 'positions',
    enableEdgeEvents: true,
    edgeProgramClasses: {
      straightNoArrow: EdgeRectangleProgram,
      straightArrow: EdgeArrowProgram,
      straightDoubleArrow: EdgeDoubleArrowProgram,
    },
  });
  sigma.on('clickNode', async (e) => {
    const res = await axios.get(`/api/topology/get?id=${e.node}`);
    nodeInfo.name = res.data.data.name;
    nodeInfo.lid = res.data.data.lid;
    nodeInfo.vendorId = res.data.data.vendorId;
    showDrawer();
    // graph.setNodeAttribute(
    //   e.node,
    //   'size',
    //   graph.getNodeAttribute(e.node, 'size') * 1.5,
    // )
  });
  // sigma.on('leaveNode', (e) =>
  //   graph.setNodeAttribute(
  //     e.node,
  //     'size',
  //     graph.getNodeAttribute(e.node, 'size') / 1.5,
  //   ),
  // );
  sigma.on('clickEdge', (e) => {
    console.log(e);
    graph.setEdgeAttribute(e.edge, 'color', 'red');
  });
});

async function routeFromSw(srcGuid, lid, handled) {
  if (handled.has(srcGuid)) {
    return;
  }
  handled.add(srcGuid);
  // if (graph.getNodeAttribute(srcGuid, '_type') === 'ca') {
  //   return;
  // }
  const resp = await axios.get(`http://localhost:3000/fib/${srcGuid}`);
  const fib = resp.data;
  if (!fib) {
    console.log(
      srcGuid,
      graph.getNodeAttribute(srcGuid, 'label'),
      'no route to',
      lid,
    );
    return;
  }
  const outPorts = fib[lid];
  outPorts.forEach((p) => {
    const outEdge = graph.findOutEdge(srcGuid, (e) => {
      const pattern = new RegExp(`^${srcGuid}:${p}->`);
      return pattern.test(e);
    });
    graph.setEdgeAttribute(outEdge, 'color', 'green');
    graph.setEdgeAttribute(outEdge, 'size', '4');
    graph.setEdgeAttribute(outEdge, 'z-index', '999');
    graph.setEdgeAttribute(outEdge, 'type', 'straightArrow');
    routeFromSw(graph.target(outEdge), lid, handled);
  });
}

async function routeFromCa(srcGuid, lid, handled) {
  if (handled.has(srcGuid)) {
    return;
  }
  handled.add(srcGuid);
  const nei = graph.directedNeighbors(srcGuid)[0];
  const edge = graph.outboundEdges(srcGuid, nei)[0];
  console.log(edge);
  graph.setEdgeAttribute(edge, 'color', 'green');
  graph.setEdgeAttribute(edge, 'size', '4');
  graph.setEdgeAttribute(edge, 'z-index', '999');
  graph.setEdgeAttribute(edge, 'type', 'straightArrow');
  routeFromSw(nei, lid, handled);
}

async function route(srcGuid, lid) {
  const handled = new Set();
  if (graph.getNodeAttribute(srcGuid, '_type') === 'ca') {
    routeFromCa(srcGuid, lid, handled);
  } else {
    routeFromSw(srcGuid, lid, handled);
  }
}
</script>

<template>
  <div style="height: 100%">
    <PageHeader
      title="拓扑关系"
      style="background: #fff; border: 1px solid rgb(235 237 240)"
    >
      <a-button type="primary" @click="route('a088c2030060365e', 1656)">
        test
      </a-button>
    </PageHeader>
    <div style="padding: 10px; background: #fff">
      <a-form
        layout="inline"
        :model="formState"
        @finish="handleFinish"
        @finish-failed="handleFinishFailed"
      >
        <a-form-item label="源">
          <a-input v-model:value="formState.user" placeholder="" />
        </a-form-item>
        <a-form-item label="Lid">
          <a-input v-model:value="formState.password" />
        </a-form-item>
        <a-form-item>
          <a-button
            type="primary"
            html-type="submit"
            :disabled="formState.user === '' || formState.password === ''"
          >
            路径查询
          </a-button>
        </a-form-item>
      </a-form>
    </div>
    <div id="cy" class="topo"></div>
    <a-drawer
      v-model:open="open"
      class="custom-class"
      root-class-name="root-class-name"
      :root-style="{ color: 'blue' }"
      style="color: red"
      title="NODE Info"
      placement="right"
    >
      <a-descriptions
        title="CA Info"
        bordered
        layout="vertical"
        :column="1"
        size="small"
      >
        <a-descriptions-item label="名称">
          {{ nodeInfo.name }}
        </a-descriptions-item>
        <a-descriptions-item label="LID">
          0x{{ nodeInfo.lid }}({{ Number.parseInt(nodeInfo.lid, 16) }})
        </a-descriptions-item>
        <a-descriptions-item label="VendorId">
          {{ nodeInfo.vendorId }}
        </a-descriptions-item>
      </a-descriptions>
    </a-drawer>
  </div>
</template>

<style lang="less" scoped>
.topo {
  height: 100%;
}
</style>
